library(shiny)
library(shinydashboard)
library(ggmap)
library(ggplot2)
library(data.table)
library(plotly)
library(waffle)
library(ggthemes)
library(extrafont)
library(rsconnect)

Periodo analizado

Se analiza el periodo Novimebre 2017 - Junio 2017 por mantenerse constante el precio del boleto en $7,50

Mapa Estaciones de subte

#setwd("~/Documents/Visualizacion")
estaciones <- read.csv('estaciones-de-subte.csv', header = TRUE)
map.bs.as <- get_map( location = "Buenos Aires"
                , zoom = 12
                , maptype = "terrain"
                , color = "bw" 
)
Map from URL : http://maps.googleapis.com/maps/api/staticmap?center=Buenos+Aires&zoom=12&size=640x640&scale=2&maptype=terrain&language=en-EN&sensor=false
Information from URL : http://maps.googleapis.com/maps/api/geocode/json?address=Buenos%20Aires&sensor=false
cols <- c("D" = "chartreuse4", "B" = "red", "C" = "blue", "H" = "darkgoldenrod1" , 'A' = "cornflowerblue", 'E' = 'blueviolet')
colors <- c("LINEA_D" = "chartreuse4", "LINEA_B" = "red", "LINEA_C" = "blue", "LINEA_H" = "darkgoldenrod1" , 'LINEA_A' = "cornflowerblue", 'LINEA_E' = 'blueviolet')

Plot 2: Ubicacion geografica de las estaciones

p <- ggmap(map.bs.as) +
      geom_point(data = estaciones ,
                    aes(x = X, y = Y,
                    colour = factor (LINEA))) +
      scale_colour_manual(values = cols) +
      theme(
      axis.title.x = element_blank(),
      axis.title.y = element_blank(),
      axis.ticks.x = element_blank(),
      axis.ticks.y = element_blank(),
      axis.text.x = element_blank(),
      axis.text.y = element_blank(),
      legend.title = element_text("LINEAS"))
p

#molinetes.2017 <- fread("molinetes_historico.csv")
#head(molinetes.2017)
#molinetes.2017.estacion.hs <- molinetes.2017[, .(prom = round(mean(TOTAL))), by = list(LINEA,ESTACION, DESDE,ID)]
#unique(filter(molinetes.2017.estacion.hs, LINEA == 'LINEA_D')$ESTACION)

Plot 3: Latido de estaciones

#molinetes.2017.estacion.hs <- molinetes.2017.estacion.hs[estaciones, on = 'ID']
#write.table(molinetes.2017.estacion.hs, "molinetes_2017_estacion_hs.csv", sep =",", col.names = TRUE, row.names = #FALSE)
molinetes.2017.estacion.hs <- read.table(file = "molinetes_2017_estacion_hs.csv", sep = ",", header = TRUE)
ax <- list(
  title = "",
  zeroline = FALSE,
  showline = FALSE,
  showticklabels = FALSE,
  showgrid = FALSE
)
p <- plot_ly(
    data = molinetes.2017.estacion.hs,
    x = ~ X, 
    y =  ~ Y, 
    size = ~ prom,
    color = ~ LINEA, 
    colors = ~ colors,
    frame = ~ DESDE, 
    text = ~ESTACION, 
    hoverinfo = "text",
    type = 'scatter',
    mode = 'markers'
  ) 
p <- layout(p,
            xaxis=ax,
            yaxis=ax)
p <- animation_opts(p, frame = 100)
p

Plot 4: Densidad pasajeros por estacion

q <- plot_ly(
    data = molinetes.2017.estacion.hs,
    x = ~ prom, 
    y =  ~ ESTACION, 
    size = ~ prom, 
    color = ~ LINEA, 
    colors = ~ colors,
    frame = ~ DESDE, 
    text = ~ESTACION, 
    hoverinfo = "text",
    type = 'scatter',
    mode = 'markers'
  ) 
q <- layout(q,
            xaxis=list(
            showline = FALSE,
            showgrid = FALSE,
            title = 'Cantidad de personas promedio'
            ),
            yaxis = list(showgrid = FALSE, title = F),
            font = list(family = "sans serif", size = 8, color = "grey")
            )
q <- q %>%
  animation_slider(currentvalue = list( font = list(color="red")))
q

Plot 4: Waffle Chart

#molinetes.2017.cant.pasajeros <- molinetes.2017[, .(sum(TOTAL)), by = PERIODO]
#molinetes.2017.cant.pasajeros.linea <- molinetes.2017[, .(cant = sum(TOTAL)), by = LINEA]
#molinetes.2017.avg.linea <- molinetes.2017[, .(avg = round(mean(TOTAL))), by = LINEA]
#write.table(molinetes.2017.avg.linea, "molinetes_2017_avg_linea.csv", sep =",", col.names = TRUE, row.names = #FALSE)
molinetes.2017.avg.linea <- read.table(file = "molinetes_2017_avg_linea.csv", sep = ",", header = TRUE)
vals = as.vector(unlist (round(molinetes.2017.avg.linea[,"avg"]), use.names = TRUE))
names (vals) = unlist (molinetes.2017.avg.linea[,"LINEA"], use.names = FALSE)

use_glyph = ‘user’, glyph_size = 5,

waffle(vals , rows = 5, colors = colors , size = 0.5, xlab = "1 icono == 1 persona", legend_pos = "down")

Shiny Dashboard

skin <- "yellow"
header <-  dashboardHeader(title = span(tagList(icon("subway", lib ="font-awesome"), 
                          "SUBTES Ciudad de Buenos Aires - Maria Ines Aran")),
                          titleWidth = 550)
sidebar <- dashboardSidebar(disable = TRUE)
                            
body <- dashboardBody(
                      
                      fluidRow(box(h4("Analisis de cantidad de pasajeros que ingresan a las estaciones de subte de la Ciudad de Buenos Aires en el periodo Enero a Septiembre 2017. Basado en datos publicados por https://data.buenosaires.gob.ar/
"),
                                   width = 12)),
                      fluidRow(box(h6("Trabajo final de la materia Visualizacion - Esp. Cs. de datos del ITBA - Maria Ines Aran"),
                                   width = 12)),
                      fluidRow(
                               box(title = "Ubicacion geografica de lineas de subte de Ciudad de Buenos Aires",
                                  background = "yellow",
                                  plotOutput("plot2")),
                               box(title = "Pasajeros por linea",
                                   "Cantidad de pasajeros promedio por linea de subtes de Ciudad de Buenos Aires   durante 2017",
                                   background = "yellow",
                                   width = 6,
                                   solidHeader = TRUE,
                                   plotOutput("plot1", height = 250))),
                      fluidRow(
                              box( title = "Densidad de pasajeros por horario (Play!)",
                                   background = "yellow",
                                   status = "warning",
                                   width = 12,
                                   plotlyOutput("plot4", height = 1000),verbatimTextOutput("event")))
                      
                      )
                

Shiny App

ui <- dashboardPage(
      header,
      sidebar,
      body,
      skin = skin)
server <- function(input, output) {
  #Plot 1
  output$plot1 <- renderPlot({
    waffle(vals , rows = 5, colors = colors , size = 0.5, xlab = "1 icono == 1 persona", legend_pos = "down")
  })
  #Plot 2
  output$plot2 <- renderPlot({
    ggmap(map.bs.as) +
      geom_point(data = estaciones ,
                    aes(x = X, y = Y,
                    colour = factor (LINEA))) +
      scale_colour_manual(values = cols) +
      theme(
      axis.title.x = element_blank(),
      axis.title.y = element_blank(),
      axis.ticks.x = element_blank(),
      axis.ticks.y = element_blank(),
      axis.text.x = element_blank(),
      axis.text.y = element_blank(),
      legend.title = element_text("LINEAS"))
  })
  #Plot 4
  output$plot4 <- renderPlotly({
    q <- plot_ly(
    data = molinetes.2017.estacion.hs,
    x = ~ prom, 
    y =  ~ ESTACION, 
    size = ~ prom, 
    color = ~ LINEA, 
    colors = ~ colors,
    frame = ~ DESDE, 
    text = ~ESTACION, 
    hoverinfo = "text",
    type = 'scatter',
    mode = 'markers'
  ) 
q <- layout(q,
            xaxis=list(
            showline = FALSE,
            showgrid = FALSE,
            title = 'Cantidad de personas promedio'
            ),
            yaxis = list(showgrid = FALSE, title = F),
            font = list(family = "sans serif", size = 8, color = "grey")
            )
q <- q %>%
  animation_slider(currentvalue = list( font = list(color="red")))    
  })
  
  
}
options(shiny.sanitize.errors = TRUE)
shinyApp(ui, server)

Listening on http://127.0.0.1:5917
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6IGRlZmF1bHQKICBodG1sX25vdGVib29rOiBkZWZhdWx0CiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0Ci0tLQpgYGB7cn0KbGlicmFyeShzaGlueSkKbGlicmFyeShzaGlueWRhc2hib2FyZCkKbGlicmFyeShnZ21hcCkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGRhdGEudGFibGUpCmxpYnJhcnkocGxvdGx5KQpsaWJyYXJ5KHdhZmZsZSkKbGlicmFyeShnZ3RoZW1lcykKbGlicmFyeShleHRyYWZvbnQpCmxpYnJhcnkocnNjb25uZWN0KQpgYGAKCgojI1BlcmlvZG8gYW5hbGl6YWRvClNlIGFuYWxpemEgZWwgcGVyaW9kbyAgTm92aW1lYnJlIDIwMTcgLSBKdW5pbyAyMDE3IHBvciBtYW50ZW5lcnNlIGNvbnN0YW50ZSBlbCBwcmVjaW8gZGVsIGJvbGV0byBlbiAkNyw1MAoKIyNNYXBhIEVzdGFjaW9uZXMgZGUgc3VidGUKCmBgYHtyfQojc2V0d2QoIn4vRG9jdW1lbnRzL1Zpc3VhbGl6YWNpb24iKQplc3RhY2lvbmVzIDwtIHJlYWQuY3N2KCdlc3RhY2lvbmVzLWRlLXN1YnRlLmNzdicsIGhlYWRlciA9IFRSVUUpCmBgYAoKYGBge3J9Cm1hcC5icy5hcyA8LSBnZXRfbWFwKCBsb2NhdGlvbiA9ICJCdWVub3MgQWlyZXMiCiAgICAgICAgICAgICAgICAsIHpvb20gPSAxMgogICAgICAgICAgICAgICAgLCBtYXB0eXBlID0gInRlcnJhaW4iCiAgICAgICAgICAgICAgICAsIGNvbG9yID0gImJ3IiAKKQpgYGAKCmBgYHtyfQpjb2xzIDwtIGMoIkQiID0gImNoYXJ0cmV1c2U0IiwgIkIiID0gInJlZCIsICJDIiA9ICJibHVlIiwgIkgiID0gImRhcmtnb2xkZW5yb2QxIiAsICdBJyA9ICJjb3JuZmxvd2VyYmx1ZSIsICdFJyA9ICdibHVldmlvbGV0JykKYGBgCgpgYGB7cn0KY29sb3JzIDwtIGMoIkxJTkVBX0QiID0gImNoYXJ0cmV1c2U0IiwgIkxJTkVBX0IiID0gInJlZCIsICJMSU5FQV9DIiA9ICJibHVlIiwgIkxJTkVBX0giID0gImRhcmtnb2xkZW5yb2QxIiAsICdMSU5FQV9BJyA9ICJjb3JuZmxvd2VyYmx1ZSIsICdMSU5FQV9FJyA9ICdibHVldmlvbGV0JykKYGBgCgojUGxvdCAyOiBVYmljYWNpb24gZ2VvZ3JhZmljYSBkZSBsYXMgZXN0YWNpb25lcwpgYGB7cn0KcCA8LSBnZ21hcChtYXAuYnMuYXMpICsKICAgICAgZ2VvbV9wb2ludChkYXRhID0gZXN0YWNpb25lcyAsCiAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBYLCB5ID0gWSwKICAgICAgICAgICAgICAgICAgICBjb2xvdXIgPSBmYWN0b3IgKExJTkVBKSkpICsKICAgICAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjb2xzKSArCiAgICAgIHRoZW1lKAogICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KCJMSU5FQVMiKSkKcApgYGAKCmBgYHtyfQojbW9saW5ldGVzLjIwMTcgPC0gZnJlYWQoIm1vbGluZXRlc19oaXN0b3JpY28uY3N2IikKYGBgCgpgYGB7cn0KI2hlYWQobW9saW5ldGVzLjIwMTcpCmBgYAoKYGBge3J9CiNtb2xpbmV0ZXMuMjAxNy5lc3RhY2lvbi5ocyA8LSBtb2xpbmV0ZXMuMjAxN1ssIC4ocHJvbSA9IHJvdW5kKG1lYW4oVE9UQUwpKSksIGJ5ID0gbGlzdChMSU5FQSxFU1RBQ0lPTiwgREVTREUsSUQpXQpgYGAKCgpgYGB7cn0KI3VuaXF1ZShmaWx0ZXIobW9saW5ldGVzLjIwMTcuZXN0YWNpb24uaHMsIExJTkVBID09ICdMSU5FQV9EJykkRVNUQUNJT04pCmBgYAoKI1Bsb3QgMzogTGF0aWRvIGRlIGVzdGFjaW9uZXMKYGBge3J9CiNtb2xpbmV0ZXMuMjAxNy5lc3RhY2lvbi5ocyA8LSBtb2xpbmV0ZXMuMjAxNy5lc3RhY2lvbi5oc1tlc3RhY2lvbmVzLCBvbiA9ICdJRCddCmBgYAoKYGBge3J9CiN3cml0ZS50YWJsZShtb2xpbmV0ZXMuMjAxNy5lc3RhY2lvbi5ocywgIm1vbGluZXRlc18yMDE3X2VzdGFjaW9uX2hzLmNzdiIsIHNlcCA9IiwiLCBjb2wubmFtZXMgPSBUUlVFLCByb3cubmFtZXMgPSAjRkFMU0UpCmBgYAoKYGBge3J9Cm1vbGluZXRlcy4yMDE3LmVzdGFjaW9uLmhzIDwtIHJlYWQudGFibGUoZmlsZSA9ICJtb2xpbmV0ZXNfMjAxN19lc3RhY2lvbl9ocy5jc3YiLCBzZXAgPSAiLCIsIGhlYWRlciA9IFRSVUUpCmBgYAoKCmBgYHtyfQpheCA8LSBsaXN0KAogIHRpdGxlID0gIiIsCiAgemVyb2xpbmUgPSBGQUxTRSwKICBzaG93bGluZSA9IEZBTFNFLAogIHNob3d0aWNrbGFiZWxzID0gRkFMU0UsCiAgc2hvd2dyaWQgPSBGQUxTRQopCmBgYAoKCmBgYHtyfQpwIDwtIHBsb3RfbHkoCiAgICBkYXRhID0gbW9saW5ldGVzLjIwMTcuZXN0YWNpb24uaHMsCiAgICB4ID0gfiBYLCAKICAgIHkgPSAgfiBZLCAKICAgIHNpemUgPSB+IHByb20sCiAgICBjb2xvciA9IH4gTElORUEsIAogICAgY29sb3JzID0gfiBjb2xvcnMsCiAgICBmcmFtZSA9IH4gREVTREUsIAogICAgdGV4dCA9IH5FU1RBQ0lPTiwgCiAgICBob3ZlcmluZm8gPSAidGV4dCIsCiAgICB0eXBlID0gJ3NjYXR0ZXInLAogICAgbW9kZSA9ICdtYXJrZXJzJwogICkgCgpwIDwtIGxheW91dChwLAogICAgICAgICAgICB4YXhpcz1heCwKICAgICAgICAgICAgeWF4aXM9YXgpCnAgPC0gYW5pbWF0aW9uX29wdHMocCwgZnJhbWUgPSAxMDApCnAKYGBgCgoKIyBQbG90IDQ6IERlbnNpZGFkIHBhc2FqZXJvcyBwb3IgZXN0YWNpb24KYGBge3J9CnEgPC0gcGxvdF9seSgKICAgIGRhdGEgPSBtb2xpbmV0ZXMuMjAxNy5lc3RhY2lvbi5ocywKICAgIHggPSB+IHByb20sIAogICAgeSA9ICB+IEVTVEFDSU9OLCAKICAgIHNpemUgPSB+IHByb20sIAogICAgY29sb3IgPSB+IExJTkVBLCAKICAgIGNvbG9ycyA9IH4gY29sb3JzLAogICAgZnJhbWUgPSB+IERFU0RFLCAKICAgIHRleHQgPSB+RVNUQUNJT04sIAogICAgaG92ZXJpbmZvID0gInRleHQiLAogICAgdHlwZSA9ICdzY2F0dGVyJywKICAgIG1vZGUgPSAnbWFya2VycycKICApIAoKcSA8LSBsYXlvdXQocSwKICAgICAgICAgICAgeGF4aXM9bGlzdCgKICAgICAgICAgICAgc2hvd2xpbmUgPSBGQUxTRSwKICAgICAgICAgICAgc2hvd2dyaWQgPSBGQUxTRSwKICAgICAgICAgICAgdGl0bGUgPSAnQ2FudGlkYWQgZGUgcGVyc29uYXMgcHJvbWVkaW8nCiAgICAgICAgICAgICksCiAgICAgICAgICAgIHlheGlzID0gbGlzdChzaG93Z3JpZCA9IEZBTFNFLCB0aXRsZSA9IEYpLAogICAgICAgICAgICBmb250ID0gbGlzdChmYW1pbHkgPSAic2FucyBzZXJpZiIsIHNpemUgPSA4LCBjb2xvciA9ICJncmV5IikKICAgICAgICAgICAgKQpxIDwtIHEgJT4lCiAgYW5pbWF0aW9uX3NsaWRlcihjdXJyZW50dmFsdWUgPSBsaXN0KCBmb250ID0gbGlzdChjb2xvcj0icmVkIikpKQpxCmBgYAoKCiNQbG90IDQ6IFdhZmZsZSBDaGFydApgYGB7cn0KI21vbGluZXRlcy4yMDE3LmNhbnQucGFzYWplcm9zIDwtIG1vbGluZXRlcy4yMDE3WywgLihzdW0oVE9UQUwpKSwgYnkgPSBQRVJJT0RPXQpgYGAKCmBgYHtyfQojbW9saW5ldGVzLjIwMTcuY2FudC5wYXNhamVyb3MubGluZWEgPC0gbW9saW5ldGVzLjIwMTdbLCAuKGNhbnQgPSBzdW0oVE9UQUwpKSwgYnkgPSBMSU5FQV0KYGBgCgpgYGB7cn0KI21vbGluZXRlcy4yMDE3LmF2Zy5saW5lYSA8LSBtb2xpbmV0ZXMuMjAxN1ssIC4oYXZnID0gcm91bmQobWVhbihUT1RBTCkpKSwgYnkgPSBMSU5FQV0KYGBgCgpgYGB7cn0KI3dyaXRlLnRhYmxlKG1vbGluZXRlcy4yMDE3LmF2Zy5saW5lYSwgIm1vbGluZXRlc18yMDE3X2F2Z19saW5lYS5jc3YiLCBzZXAgPSIsIiwgY29sLm5hbWVzID0gVFJVRSwgcm93Lm5hbWVzID0gI0ZBTFNFKQpgYGAKCmBgYHtyfQptb2xpbmV0ZXMuMjAxNy5hdmcubGluZWEgPC0gcmVhZC50YWJsZShmaWxlID0gIm1vbGluZXRlc18yMDE3X2F2Z19saW5lYS5jc3YiLCBzZXAgPSAiLCIsIGhlYWRlciA9IFRSVUUpCmBgYAoKYGBge3J9CnZhbHMgPSBhcy52ZWN0b3IodW5saXN0IChyb3VuZChtb2xpbmV0ZXMuMjAxNy5hdmcubGluZWFbLCJhdmciXSksIHVzZS5uYW1lcyA9IFRSVUUpKQpuYW1lcyAodmFscykgPSB1bmxpc3QgKG1vbGluZXRlcy4yMDE3LmF2Zy5saW5lYVssIkxJTkVBIl0sIHVzZS5uYW1lcyA9IEZBTFNFKQpgYGAKCgp1c2VfZ2x5cGggPSAndXNlcicsIGdseXBoX3NpemUgPSA1LAoKYGBge3J9CndhZmZsZSh2YWxzICwgcm93cyA9IDUsIGNvbG9ycyA9IGNvbG9ycyAsIHNpemUgPSAwLjUsIHhsYWIgPSAiMSBpY29ubyA9PSAxIHBlcnNvbmEiLCBsZWdlbmRfcG9zID0gImRvd24iKQpgYGAKCiNTaGlueSBEYXNoYm9hcmQKCmBgYHtyfQpza2luIDwtICJ5ZWxsb3ciCmhlYWRlciA8LSAgZGFzaGJvYXJkSGVhZGVyKHRpdGxlID0gc3Bhbih0YWdMaXN0KGljb24oInN1YndheSIsIGxpYiA9ImZvbnQtYXdlc29tZSIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAiU1VCVEVTIENpdWRhZCBkZSBCdWVub3MgQWlyZXMgLSBNYXJpYSBJbmVzIEFyYW4iKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgdGl0bGVXaWR0aCA9IDU1MCkKc2lkZWJhciA8LSBkYXNoYm9hcmRTaWRlYmFyKGRpc2FibGUgPSBUUlVFKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgCmJvZHkgPC0gZGFzaGJvYXJkQm9keSgKICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgZmx1aWRSb3coYm94KGg0KCJBbmFsaXNpcyBkZSBjYW50aWRhZCBkZSBwYXNhamVyb3MgcXVlIGluZ3Jlc2FuIGEgbGFzIGVzdGFjaW9uZXMgZGUgc3VidGUgZGUgbGEgQ2l1ZGFkIGRlIEJ1ZW5vcyBBaXJlcyBlbiBlbCBwZXJpb2RvIEVuZXJvIGEgU2VwdGllbWJyZSAyMDE3LiBCYXNhZG8gZW4gZGF0b3MgcHVibGljYWRvcyBwb3IgaHR0cHM6Ly9kYXRhLmJ1ZW5vc2FpcmVzLmdvYi5hci8KIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2lkdGggPSAxMikpLAogICAgICAgICAgICAgICAgICAgICAgZmx1aWRSb3coYm94KGg2KCJUcmFiYWpvIGZpbmFsIGRlIGxhIG1hdGVyaWEgVmlzdWFsaXphY2lvbiAtIEVzcC4gQ3MuIGRlIGRhdG9zIGRlbCBJVEJBIC0gTWFyaWEgSW5lcyBBcmFuIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2lkdGggPSAxMikpLAogICAgICAgICAgICAgICAgICAgICAgZmx1aWRSb3coCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBib3godGl0bGUgPSAiVWJpY2FjaW9uIGdlb2dyYWZpY2EgZGUgbGluZWFzIGRlIHN1YnRlIGRlIENpdWRhZCBkZSBCdWVub3MgQWlyZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmFja2dyb3VuZCA9ICJ5ZWxsb3ciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxvdE91dHB1dCgicGxvdDIiKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBib3godGl0bGUgPSAiUGFzYWplcm9zIHBvciBsaW5lYSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNhbnRpZGFkIGRlIHBhc2FqZXJvcyBwcm9tZWRpbyBwb3IgbGluZWEgZGUgc3VidGVzIGRlIENpdWRhZCBkZSBCdWVub3MgQWlyZXMgICBkdXJhbnRlIDIwMTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJhY2tncm91bmQgPSAieWVsbG93IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3aWR0aCA9IDYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc29saWRIZWFkZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBsb3RPdXRwdXQoInBsb3QxIiwgaGVpZ2h0ID0gMjUwKSkpLAogICAgICAgICAgICAgICAgICAgICAgZmx1aWRSb3coCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJveCggdGl0bGUgPSAiRGVuc2lkYWQgZGUgcGFzYWplcm9zIHBvciBob3JhcmlvIChQbGF5ISkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJhY2tncm91bmQgPSAieWVsbG93IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGF0dXMgPSAid2FybmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2lkdGggPSAxMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwbG90bHlPdXRwdXQoInBsb3Q0IiwgaGVpZ2h0ID0gMTAwMCksdmVyYmF0aW1UZXh0T3V0cHV0KCJldmVudCIpKSkKICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgCmBgYAoKI1NoaW55IEFwcApgYGB7cn0KdWkgPC0gZGFzaGJvYXJkUGFnZSgKICAgICAgaGVhZGVyLAogICAgICBzaWRlYmFyLAogICAgICBib2R5LAogICAgICBza2luID0gc2tpbikKCnNlcnZlciA8LSBmdW5jdGlvbihpbnB1dCwgb3V0cHV0KSB7CiAgI1Bsb3QgMQogIG91dHB1dCRwbG90MSA8LSByZW5kZXJQbG90KHsKICAgIHdhZmZsZSh2YWxzICwgcm93cyA9IDUsIGNvbG9ycyA9IGNvbG9ycyAsIHNpemUgPSAwLjUsIHhsYWIgPSAiMSBpY29ubyA9PSAxIHBlcnNvbmEiLCBsZWdlbmRfcG9zID0gImRvd24iKQogIH0pCiAgI1Bsb3QgMgogIG91dHB1dCRwbG90MiA8LSByZW5kZXJQbG90KHsKICAgIGdnbWFwKG1hcC5icy5hcykgKwogICAgICBnZW9tX3BvaW50KGRhdGEgPSBlc3RhY2lvbmVzICwKICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IFgsIHkgPSBZLAogICAgICAgICAgICAgICAgICAgIGNvbG91ciA9IGZhY3RvciAoTElORUEpKSkgKwogICAgICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGNvbHMpICsKICAgICAgdGhlbWUoCiAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoIkxJTkVBUyIpKQogIH0pCiAgI1Bsb3QgNAogIG91dHB1dCRwbG90NCA8LSByZW5kZXJQbG90bHkoewogICAgcSA8LSBwbG90X2x5KAogICAgZGF0YSA9IG1vbGluZXRlcy4yMDE3LmVzdGFjaW9uLmhzLAogICAgeCA9IH4gcHJvbSwgCiAgICB5ID0gIH4gRVNUQUNJT04sIAogICAgc2l6ZSA9IH4gcHJvbSwgCiAgICBjb2xvciA9IH4gTElORUEsIAogICAgY29sb3JzID0gfiBjb2xvcnMsCiAgICBmcmFtZSA9IH4gREVTREUsIAogICAgdGV4dCA9IH5FU1RBQ0lPTiwgCiAgICBob3ZlcmluZm8gPSAidGV4dCIsCiAgICB0eXBlID0gJ3NjYXR0ZXInLAogICAgbW9kZSA9ICdtYXJrZXJzJwogICkgCgpxIDwtIGxheW91dChxLAogICAgICAgICAgICB4YXhpcz1saXN0KAogICAgICAgICAgICBzaG93bGluZSA9IEZBTFNFLAogICAgICAgICAgICBzaG93Z3JpZCA9IEZBTFNFLAogICAgICAgICAgICB0aXRsZSA9ICdDYW50aWRhZCBkZSBwZXJzb25hcyBwcm9tZWRpbycKICAgICAgICAgICAgKSwKICAgICAgICAgICAgeWF4aXMgPSBsaXN0KHNob3dncmlkID0gRkFMU0UsIHRpdGxlID0gRiksCiAgICAgICAgICAgIGZvbnQgPSBsaXN0KGZhbWlseSA9ICJzYW5zIHNlcmlmIiwgc2l6ZSA9IDgsIGNvbG9yID0gImdyZXkiKQogICAgICAgICAgICApCnEgPC0gcSAlPiUKICBhbmltYXRpb25fc2xpZGVyKGN1cnJlbnR2YWx1ZSA9IGxpc3QoIGZvbnQgPSBsaXN0KGNvbG9yPSJyZWQiKSkpICAgIAogIH0pCiAgCiAgCn0KCm9wdGlvbnMoc2hpbnkuc2FuaXRpemUuZXJyb3JzID0gVFJVRSkKCnNoaW55QXBwKHVpLCBzZXJ2ZXIpCmBgYAoKCg==